<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title></title>
  </head>
  <style>
    body {
      padding: 0;
      margin: 0;
      background-color: #222;
      height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .base {
      height: 9em;
      padding: 3em;
      width: 9em;
      transform: rotateX(45deg) rotateZ(45deg);
      transform-style: preserve-3d;
      box-shadow: 0 12px 20px 6px rgb(104 112 118 / 0.2);
      border-radius: 15px;
    }

    .cube,
    .cube:after,
    .cube:before {
      content: "";
      float: left;
      height: 3em;
      position: absolute;
      width: 3em;
    }

    .cube {
      background-color: #8971aa;
      position: relative;
      transform: translateZ(3em);
      transform-style: preserve-3d;
      transition: 0.25s;
      box-shadow: 10em 10em 1.5em rgba(0, 0, 0, 0.1);
      animation: anim 1s infinite;
    }

    .cube:after {
      background-color: #72697f;
      transform: rotateX(-90deg) translateY(3em);
      transform-origin: 100% 100%;
    }

    .cube:before {
      background-color: #a79bbc;
      transform: rotateY(90deg) translateX(3em);
      transform-origin: 100% 0;
    }

    .cube:nth-child(1) {
      animation-delay: 0.05s;
    }

    .cube:nth-child(2) {
      animation-delay: 0.1s;
    }

    .cube:nth-child(3) {
      animation-delay: 0.15s;
    }

    .cube:nth-child(4) {
      animation-delay: 0.2s;
    }

    .cube:nth-child(5) {
      animation-delay: 0.25s;
    }

    .cube:nth-child(6) {
      animation-delay: 0.3s;
    }

    .cube:nth-child(7) {
      animation-delay: 0.35s;
    }

    .cube:nth-child(8) {
      animation-delay: 0.4s;
    }

    .cube:nth-child(9) {
      animation-delay: 0.45s;
    }

    @keyframes anim {
      50% {
        transform: translateZ(0.5em);
      }
    }
  </style>
  <body>
    <div class="base">
      <div class="cube"></div>
      <div class="cube"></div>
      <div class="cube"></div>
      <div class="cube"></div>
      <div class="cube"></div>
      <div class="cube"></div>
      <div class="cube"></div>
      <div class="cube"></div>
      <div class="cube"></div>
    </div>
  </body>
  <script>
    // 鼠标烟花效果
    class Circle {
      constructor({ origin, speed, color, angle, context }) {
        this.origin = origin;
        this.position = {
          ...this.origin,
        };
        this.color = color;
        this.speed = speed;
        this.angle = angle;
        this.context = context;
        this.renderCount = 0;
      }

      draw() {
        this.context.fillStyle = this.color;
        this.context.beginPath();
        this.context.arc(this.position.x, this.position.y, 2, 0, Math.PI * 2);
        this.context.fill();
      }

      move() {
        this.position.x = Math.sin(this.angle) * this.speed + this.position.x;
        this.position.y =
          Math.cos(this.angle) * this.speed +
          this.position.y +
          this.renderCount * 0.3;
        this.renderCount++;
      }
    }

    class Boom {
      constructor({ origin, context, circleCount = 10, area }) {
        this.origin = origin;
        this.context = context;
        this.circleCount = circleCount;
        this.area = area;
        this.stop = false;
        this.circles = [];
      }

      randomArray(range) {
        const length = range.length;
        const randomIndex = Math.floor(length * Math.random());
        return range[randomIndex];
      }

      randomColor() {
        const range = ["8", "9", "A", "B", "C", "D", "E", "F"];
        return (
          "#" +
          this.randomArray(range) +
          this.randomArray(range) +
          this.randomArray(range) +
          this.randomArray(range) +
          this.randomArray(range) +
          this.randomArray(range)
        );
      }

      randomRange(start, end) {
        return (end - start) * Math.random() + start;
      }

      init() {
        for (let i = 0; i < this.circleCount; i++) {
          const circle = new Circle({
            context: this.context,
            origin: this.origin,
            color: this.randomColor(),
            angle: this.randomRange(Math.PI - 1, Math.PI + 1),
            speed: this.randomRange(1, 6),
          });
          this.circles.push(circle);
        }
      }

      move() {
        this.circles.forEach((circle, index) => {
          if (
            circle.position.x > this.area.width ||
            circle.position.y > this.area.height
          ) {
            return this.circles.splice(index, 1);
          }
          circle.move();
        });
        if (this.circles.length == 0) {
          this.stop = true;
        }
      }

      draw() {
        this.circles.forEach((circle) => circle.draw());
      }
    }

    class CursorSpecialEffects {
      constructor() {
        this.computerCanvas = document.createElement("canvas");
        this.renderCanvas = document.createElement("canvas");

        this.computerContext = this.computerCanvas.getContext("2d");
        this.renderContext = this.renderCanvas.getContext("2d");

        this.globalWidth = window.innerWidth;
        this.globalHeight = window.innerHeight;

        this.booms = [];
        this.running = false;
      }

      handleMouseDown(e) {
        const boom = new Boom({
          origin: {
            x: e.clientX,
            y: e.clientY,
          },
          context: this.computerContext,
          area: {
            width: this.globalWidth,
            height: this.globalHeight,
          },
        });
        boom.init();
        this.booms.push(boom);
        this.running || this.run();
      }

      handlePageHide() {
        this.booms = [];
        this.running = false;
      }

      init() {
        const style = this.renderCanvas.style;
        style.position = "fixed";
        style.top = style.left = 0;
        style.zIndex = "999999999999999999999999999999999999999999";
        style.pointerEvents = "none";

        style.width =
          this.renderCanvas.width =
          this.computerCanvas.width =
            this.globalWidth;
        style.height =
          this.renderCanvas.height =
          this.computerCanvas.height =
            this.globalHeight;

        document.body.append(this.renderCanvas);

        window.addEventListener("mousedown", this.handleMouseDown.bind(this));
        window.addEventListener("pagehide", this.handlePageHide.bind(this));
      }

      run() {
        this.running = true;
        if (this.booms.length == 0) {
          return (this.running = false);
        }

        requestAnimationFrame(this.run.bind(this));

        this.computerContext.clearRect(
          0,
          0,
          this.globalWidth,
          this.globalHeight
        );
        this.renderContext.clearRect(0, 0, this.globalWidth, this.globalHeight);

        this.booms.forEach((boom, index) => {
          if (boom.stop) {
            return this.booms.splice(index, 1);
          }
          boom.move();
          boom.draw();
        });
        this.renderContext.drawImage(
          this.computerCanvas,
          0,
          0,
          this.globalWidth,
          this.globalHeight
        );
      }
    }

    const cursorSpecialEffects = new CursorSpecialEffects();
    cursorSpecialEffects.init();
  </script>
</html>
